﻿using System;
using System.Reflection;
using System.Text.RegularExpressions;
using AcceptanceTest.Framework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Data;
using System.Collections.Generic;

namespace AcceptanceTest.Framework.UnitTests
{
    
    
    /// <summary>
    ///This is a test class for TestRunnerTest and is intended
    ///to contain all TestRunnerTest Unit Tests
    ///</summary>
    [TestClass()]
    public class TestRunnerTest
    {


        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion


        /// <summary>
        ///A test for ParseParameters
        ///</summary>
        [TestMethod()]
        [DeploymentItem("AcceptanceTest.Framework.dll")]
        public void LoadTest()
        {
            var steps = new Steps();
            TestRunner_Accessor runner = new TestRunner_Accessor();
            runner.LoadSteps(steps);

            Assert.IsTrue(runner.stepDefinitions.ContainsKey(@"^the order (\d+)$"));
            Assert.AreEqual("SetOrderId", runner.stepDefinitions[@"^the order (\d+)$"].Name);

            runner.ExecuteStep(@"the order 12345");

            Assert.AreEqual(12345, steps.OrderId);
        }

        [TestMethod()]
        public void LoadTest2()
        {
            var steps = new Steps();
            TestRunner_Accessor runner = new TestRunner_Accessor();
            runner.LoadSteps(steps);

            runner.ExecuteStep("the form 12345x");
            Assert.AreEqual("12345x", steps.Form);

            runner.ExecuteStep("the form \"12345 x\"");
            Assert.AreEqual("\"12345 x\"", steps.Form);
        }

        [TestMethod()]
        public void LoadTest3()
        {
            var steps = new Steps();
            TestRunner_Accessor runner = new TestRunner_Accessor();
            runner.LoadSteps(steps);

            runner.ExecuteStep("the score 4.5 at 1/Jan/2010");
            Assert.AreEqual(new DateTime(2010,1,1), steps.Time);
            Assert.AreEqual(4.5, steps.Score);
        }

        //[TestMethod()]
        //public void TestCreateScenario()
        //{
        //    DataSet ds = CreateTestDataSet();

        //    var scenario = new Scenario
        //    {
        //        Name = "Test with [table]",
        //        Steps = new string[]{
        //            "order id <id>", 
        //            "score <score>",
        //            "order id <id> and score <score>",
        //        },
        //    };
        //    TestRunner_Accessor runner = new TestRunner_Accessor();
        //    var sc = runner.CreateScenario(scenario, ds.Tables[0].Rows[1]);

        //    //Assert.AreEqual("Test with [table] #1", sc.Name);

        //    Assert.AreEqual("order id 12345", sc.Steps[0]);
        //    Assert.AreEqual("score 4.5", sc.Steps[1]);
        //    Assert.AreEqual("order id 12345 and score 4.5", sc.Steps[2]);
        //}

        private DataSet CreateTestDataSet()
        {
            DataSet ds = new DataSet();
            DataTable dt = new DataTable("table");
            dt.Columns.Add(new DataColumn { ColumnName="id", DataType=typeof(object)});
            dt.Columns.Add(new DataColumn { ColumnName = "form", DataType = typeof(object) });
            dt.Columns.Add(new DataColumn { ColumnName = "score", DataType = typeof(object) });
            dt.Columns.Add(new DataColumn { ColumnName = "time", DataType = typeof(object) });
            
            DataRow row = dt.NewRow();
            row["id"] = "100000";
            row["score"] = 84d;
            row["form"] = "AIC Drive By";
            row["time"] = "2/Jan/2010";
            dt.Rows.Add(row);

            row = dt.NewRow();
            row["id"] = "12345";
            row["score"] = 4.5d;
            row["form"] = "AIC Full";
            row["time"] = "1/Jan/2010";
            dt.Rows.Add(row);

            DataTable settings = new DataTable("settings");
            settings.Columns.Add(new DataColumn { ColumnName = "key", DataType = typeof(object) });
            settings.Columns.Add(new DataColumn { ColumnName = "value", DataType = typeof(object) });
            row = settings.NewRow();
            
            row["key"] = "url";
            row["value"] = "url-value";
            settings.Rows.Add(row);

            ds.Tables.Add(dt);
            ds.Tables.Add(settings);
            return ds;
        }

        private Scenario CreateScenario()
        {
            return new Scenario
            {
                Name = "Test with [table]",
                Steps = new string[]{
                    "the order <id>", 
                    "the form <form>",
                    "the score <score> at <time>",
                },
            };
        }

        private Feature CreateFeature(Scenario scenario)
        {
            return new Feature
            {
                Name = "Test Feature",
                Scenarios = new List<Scenario> { scenario },
            };
        }

        [TestMethod()]
        public void TestRunWithTable()
        {
            DataSet ds = CreateTestDataSet();
            var scenario = CreateScenario();
            var feature = CreateFeature(scenario);

            var steps = new Steps();
            TestRunner runner = new TestRunner();
            runner.LoadSteps(steps);
            runner.OnStatusUpdated += (_, e) =>
            {
                Console.WriteLine(string.Format("{0}: {1}", e.Status, e.Tag.ToString()));
            };

            runner.Run(feature, ds);
            Assert.AreEqual(new DateTime(2010, 1, 1), steps.Time);
            Assert.AreEqual(4.5, steps.Score);
            Assert.AreEqual(12345, steps.OrderId);
            Assert.AreEqual("AIC Full", steps.Form);

        }

        [TestMethod()]
        public void TestRunWithoutTable()
        {
            var scenario = new Scenario
            {
                Name = "Test without table",
                Steps = new string[]{
                    "the order 123", 
                    "the form \"a form name here\"",
                    "the score 4.5 at 1/Jan/2010",
                },
            };
            var feature = CreateFeature(scenario);

            var steps = new Steps();                        
            TestRunner runner = new TestRunner();
            runner.LoadSteps(steps);
            runner.OnStatusUpdated += (_, e) =>
                {
                    Console.WriteLine(string.Format("{0}: {1}", e.Status, e.Tag.ToString()));
                };

            runner.Run(feature, null);
            Assert.AreEqual(new DateTime(2010, 1, 1), steps.Time);
            Assert.AreEqual(4.5, steps.Score);
            Assert.AreEqual(123, steps.OrderId);
            Assert.AreEqual("\"a form name here\"", steps.Form);

        }
        
        [TestMethod()]
        public void TestDefinitionException_NoStep()
        {
            DataSet ds = CreateTestDataSet();
            var scenario = CreateScenario();
            scenario.Steps[0] = "the order id <id>";

            var feature = CreateFeature(scenario);
            var steps = new Steps();   
            TestRunner runner = new TestRunner();
            runner.LoadSteps(steps);
            
            bool fired = false;
            runner.OnStatusUpdated += (_, e) =>
            {
                fired |= e.Status == ExecutionStatus.Error;
                Console.WriteLine(string.Format("{0}: {1}: {2}", e.Status, 
                    e.Tag.ToString(),
                    (e.Exception==null? "": e.Exception.Message)));
            };
            runner.Run(feature, ds);

            Assert.IsTrue(fired);
        }

        [TestMethod()]
        public void TestExecutionException()
        {
            DataSet ds = CreateTestDataSet();
            var scenario = CreateScenario();
            scenario.Steps[0] = "there is an error";

            var feature = CreateFeature(scenario);
            var steps = new Steps();
            TestRunner runner = new TestRunner();
            runner.LoadSteps(steps);

            bool fired = false;
            runner.OnStatusUpdated += (_, e) =>
            {
                fired |= e.Status == ExecutionStatus.Failed;
                Console.WriteLine(string.Format("{0}: {1}: {2}", e.Status,
                    e.Tag.ToString(),
                    (e.Exception == null ? "" : e.Exception.Message)));
            };

            runner.Run(feature, ds);
            Assert.IsTrue(fired);
        }
       
        [TestMethod()]
        [ExpectedException(typeof(StepDefinitionException))]
        public void TestDefinitionException_NoTable()
        {
            DataSet ds = CreateTestDataSet();
            var scenario = CreateScenario();
            scenario.Name = "test [table1]";

            var feature = CreateFeature(scenario);
            var steps = new Steps();
            TestRunner runner = new TestRunner();
            runner.LoadSteps(steps);

            bool fired = false;
            runner.OnStatusUpdated += (_, e) =>
            {
                fired |= e.Status == ExecutionStatus.Error;
                Console.WriteLine(string.Format("{0}: {1}: {2}", e.Status,
                    e.Tag.ToString(),
                    (e.Exception == null ? "" : e.Exception.Message)));
            };
            runner.Run(feature, ds);

            Assert.IsTrue(fired);
        }

        [TestMethod()]
        public void TestConfig()
        {
            DataSet ds = CreateTestDataSet();
            var steps = new Steps();
            TestRunner_Accessor runner = new TestRunner_Accessor();
            runner.Config(steps, ds.Tables["settings"]);

            Assert.AreEqual("url-value", steps.Url);
        }
    }
}
